package com.pf.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * 可参考博客：
 * https://www.jianshu.com/p/0d4aea41a70c
 * https://www.jianshu.com/p/a267e74f486b
 * <p>
 * 该类为了覆盖spring redis默认的RredisTemplate配置，可以搜索RedisAutoConfiguration文件查看源码。
 * 原因：
 * 引入redis的依赖后，SpringBoot自动帮我们在容器中生成RedisTemplate和StringRedisTemplate。
 * 但是这个RedisTemplate泛型是<Object,Object>。RedisTemplate没有设置数据存储在Redis时，Key和Value的序列化方式。（采用默认的JDK序列化方式）
 * 示例：
 * 1).如果采用默认的jdk序列化，将一个key-value为 bookCount = 1 的信息存入redis，则key就成了 \xAC\xED\x00\x05t\x00
 * 因为源码中有使用@ConditionalOnMissingBean(name = "redisTemplate")注解，所以如果spring容器中再有一个name为redisTemplate的RedisTemplate对象，那么源码中自动配置的RedisTemplate就不会实例化，就不会生效。
 * 所以我们为了覆盖源码中RedisTemplate，方法名一定要叫redisTemplate 因为@Bean注解是根据方法名配置这个bean的name的，覆盖默认配置。
 * 我们需要设置这个RedisTemplate在数据存在时key和value的序列化方式（默认是JdkSerializationRedisSerializer，这样会导致
 * 通过redis desktop manager显示我们的key和value时显示不正常字符）
 *
 * @author PF
 * @date 2021/5/13 22:18
 * @Version 1.0
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String,Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        //序列化操作
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        //设置所有访问权限以及所有的实际类型都可序列化和反序列化
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //key采用string的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key采用string的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}
